#include "client.h"
#include "log.h"

using boost::asio::ip::tcp;


Client::Client(boost::asio::io_service& io_service,
               tcp::resolver::iterator endpoint_iterator,
               RxMsg_ptr inRxMsgPtr)
        : io_service_(io_service),
        strand_(io_service),
        socket_(io_service),
        RxMsg(inRxMsgPtr)
{
    tcp::endpoint endpoint = *endpoint_iterator;
    socket_.async_connect(endpoint,
                          boost::bind(&Client::handle_connect, this,
                                      boost::asio::placeholders::error, ++endpoint_iterator));
}

void Client::write(const Message & msg)
{
    io_service_.post(boost::bind(&Client::do_write, this, msg));
    do_write(msg);
}

void Client::read ( )
{
    io_service_.post(boost::bind(&Client::do_read, this));
}

void Client::close()
{
    io_service_.post(boost::bind(&Client::do_close, this));
}

Message Client::getInMsg()
{
    return inMsg ;
}


void Client::handle_connect(const boost::system::error_code& error,
                            tcp::resolver::iterator endpoint_iterator)
{
    if (!error)
    {
        LOGCON.info("Connect successfully\n") ;
        do_read() ;
    }
    else if (endpoint_iterator != tcp::resolver::iterator())
    {
        socket_.close();
        tcp::endpoint endpoint = *endpoint_iterator;
        socket_.async_connect(endpoint,
                              boost::bind(&Client::handle_connect, this,
                                          boost::asio::placeholders::error, ++endpoint_iterator));
    }
}

void Client::do_read()
{
    LOGCON.info("do_read()\n") ;
    inMsg.reset() ;
    LOGCON.infoStream() << inMsg ;
    boost::asio::async_read(socket_, boost::asio::buffer(inMsg.data(), Message::HEADER_LEN),
                            strand_.wrap(
                                boost::bind(&Client::handle_read_header, shared_from_this(),
                                            boost::asio::placeholders::error)));
}

void Client::handle_read_header(const boost::system::error_code& error)
{
    LOGCON.info("handle_read_header\n") ;
    LOGCON.infoStream() << inMsg ;
    if (!error && inMsg.decode_header())
    {
        LOGCON.info("decode header sucessfully\n") ;
        LOGCON.infoStream() << inMsg ;
        boost::asio::async_read(socket_,
                                boost::asio::buffer(inMsg.body(), inMsg.body_length()),
                                strand_.wrap(boost::bind(&Client::handle_read_body, this,
                                                         boost::asio::placeholders::error)));
    }
    else
    {
        do_close();
    }
}

void Client::handle_read_body(const boost::system::error_code& error)
{
    if (!error)
    {
        LOGCON.info("handle_read_body\n") ;
        RxMsg(inMsg) ;
        boost::asio::async_read(socket_, boost::asio::buffer(inMsg.data(), Message::HEADER_LEN),
                                strand_.wrap(
                                    boost::bind(&Client::handle_read_header, shared_from_this(),
                                                boost::asio::placeholders::error)));
    }
    else
    {
        do_close();
    }
}

void Client::do_write(const Message & outMsg)
{
    boost::asio::async_write(socket_,
                             boost::asio::buffer(outMsg.data(),outMsg.size()),
                             boost::bind(&Client::handle_write, this,
                                         boost::asio::placeholders::error));
}

void Client::handle_write(const boost::system::error_code& error)
{
    if (!error)
    {
        std::cout<< "Write Sucess" << std::endl ;
    }
    else
    {
        std::cout<< "Write Error" << std::endl ;
        do_close();
    }
}

void Client::do_close()
{
    socket_.close();
}
